<template>
  <Card title="接口服务" size="small">
    <template #extra>
      <div style="position: relative; top: -3px">
        <Switch style="margin-right: 10px" v-model:checked="fileMode" un-checked-children="数据" checked-children="文件"></Switch>
        <Button type="primary" shape="round" @click="run()"> <PlayCircleOutlined /> 执行 </Button>
      </div>
    </template>
    <Input.Group compact>
      <Select v-model:value="currentTemplate" style="width: 210px">
        <Select.Option value="">默认</Select.Option>
        <Select.Option v-for="item of templates" :value="item.value">{{ item.text }}</Select.Option>
      </Select>
      <Select v-model:value="method" style="width: 100px">
        <Select.Option value="get">GET</Select.Option>
        <Select.Option value="post">POST</Select.Option>
        <Select.Option value="put">PUT</Select.Option>
        <Select.Option value="delete">DELETE</Select.Option>
      </Select>
      <AutoComplete v-model:value="api" :allow-clear="true" :options="histories" style="width: calc(100% - 370px)">
        <template #addonBefore> </template>
        <template #addonAfter>
          <HistoryOutlined />
        </template>
      </AutoComplete>
      <Input v-model:value="times" style="width: 50px" />
    </Input.Group>
  </Card>
  <Row :gutter="10">
    <Col :span="10">
      <Tabs v-model:activeKey="activeKey">
        <Tabs.TabPane key="params" tab="Params" :style="panelStyle">
          <key-values ref="paramsRef" :height="contentHeight" v-model:items="params" />
        </Tabs.TabPane>
        <Tabs.TabPane key="authorization" tab="Authorization" :style="panelStyle"> </Tabs.TabPane>
        <Tabs.TabPane key="headers" tab="Headers" :style="panelStyle"> </Tabs.TabPane>
        <Tabs.TabPane key="body" tab="Body" :style="panelStyle">
          <key-values ref="bodysRef" :height="contentHeight" v-model:items="bodys" />
        </Tabs.TabPane>
      </Tabs>
    </Col>
    <Col :span="14">
      <div class="shell headers" v-if="callStatistics > -1">
        <p>
          <b v-if="callStatistics == 0">正在执行中，请稍后</b>
          <template v-if="callStatistics > 0">
            <b>共耗时：</b><span>{{ callStatistics }}秒</span>
          </template>
        </p>
      </div>
      <div class="shell results" :style="resultStyle">
        <div v-for="item of codes" :class="item.completed" v-html="item.detail"></div>
      </div>
    </Col>
  </Row>
</template>
<script setup>
  import { useStore } from 'vuex';
  import { ref, inject, watch, computed, reactive, onMounted } from 'vue';
  import { Button, Card, Tabs, Input, AutoComplete, Row, Col, Select, Switch } from 'ant-design-vue';
  import { PlayCircleOutlined, HistoryOutlined } from '@ant-design/icons-vue';
  import { getDateTime } from '@/libs/helpers';

  import configs from '@/libs/pressure.js';

  const $store = useStore();

  const $http = inject('$http');
  const $message = inject('$message');
  const $eventBus = inject('$eventBus');
  const $copyText = inject('$copyText');

  const contentHeight = ref(400);

  const fileMode = ref(false);
  const templates = ref([]);
  const currentTemplate = ref('');

  const method = ref('get');
  const api = ref('');

  const paramsRef = ref();
  const bodysRef = ref();

  const params = ref([]);
  const bodys = ref([]);

  const times = ref(1);
  const codes = ref([]);
  const histories = ref([]);

  const callStatistics = ref(-1);
  const activeKey = ref('params');

  let results = reactive({});
  let templateInstance = {};

  const panelStyle = computed(() => {
    return {
      minHeight: contentHeight.value + 'px',
      maxHeight: contentHeight.value + 'px'
    };
  });

  const resultStyle = computed(() => {
    return {
      minHeight: contentHeight.value + 10 + 'px',
      maxHeight: contentHeight.value + 10 + 'px'
    };
  });

  watch(results, () => {
    let index = 1;
    let rs = [];
    let props = Object.getOwnPropertyNames(results);

    props.forEach(p => {
      let content = {};
      let item = results[p];
      let detail = `<b>${index++}</b><h3><b>${item.at}</b>${item.content}</h3><ul><li>`;

      detail += item.messages.join('</li><li>') + '</li>';

      if (item.elapsedTicks) {
        content.completed = item.status;
        detail += `<li>共耗时：${item.elapsedTicks}秒！</li>`;
      }

      detail += '</ul>';

      content.detail = detail;

      rs.push(content);
    });

    return (codes.value = rs);
  });

  watch(currentTemplate, () => {
    templateInstance = {};
    params.value.length = 0;
    bodys.value.length = 0;
    activeKey.value = 'params';

    if (currentTemplate.value != '') {
      templateInstance = configs.find(c => c.name == currentTemplate.value);

      fileMode.value = templateInstance.mode == 'file';

      if (templateInstance.security) {
        let items = [];
        let props = Object.getOwnPropertyNames(templateInstance.security);

        switch (templateInstance.security.type) {
          case 'params':
            items = params.value;
            params.value.length = 0;

            break;
        }

        props.forEach(p => {
          if (p != 'type') {
            items.push({ key: p, value: templateInstance.security[p] });
          }
        });
      }

      if (templateInstance.params) {
        resolveRequest(params.value, templateInstance.params);
      }

      if (templateInstance.body) {
        activeKey.value = 'body';

        resolveRequest(bodys.value, templateInstance.body);
      }
    }

    if (params.value.length == 0) {
      params.value.push({ key: '', value: '' });
    }

    if (bodys.value.length == 0) {
      bodys.value.push({ key: '', value: '' });
    }
  });

  function ResizeChartPanel(area) {
    contentHeight.value = area.height - 180;
  }

  function run() {
    if (!api.value) {
      callStatistics.value = -1;
      $message.warning({ key: 'validate', content: '请输入接口地址！' });

      return;
    }

    callStatistics.value = 0;

    Object.keys(results).map(key => delete results[key]);

    let url = getHttpUrl();

    if ((templateInstance.body || {}).subQuery) {
      runWithSubQuery(templateInstance.body.subQuery, url);

      return;
    }

    let httpConfigs = [];

    for (let i = 0; i < times.value; i++) {
      let config = {
        method: method.value,
        url: url,
        headers: {},
        key: i,
        begin: new Date().getTime(),
        responseType: fileMode.value ? 'blob' : 'json'
      };

      results[i] = {
        at: getDateTime(),
        content: `调用接口 - ${url}`,
        messages: [`[${getDateTime()}] 开始执行...`]
      };

      httpConfigs.push(config);
    }

    addTestHistory();
    invokeHttpRequest(httpConfigs);
    loadHistories();
  }

  function runWithSubQuery(meta, url) {
    let realUrl = '';
    let splitIndex = url.indexOf('?');
    let fullValues = bodysRef.value.getFullValues();

    if (splitIndex != -1) {
      realUrl = url.substring(0, splitIndex) + fullValues.subQuery + url.substring(splitIndex);
    } else {
      realUrl = url + fullValues.subQuery;
    }

    let requestDatas = bodysRef.value.build();

    $http[meta.method || 'post'](realUrl, requestDatas).then(res => {
      let datas = (res.data || {}).dataList || [];
      let httpConfigs = [];

      if (meta.type == 'multi') {
        let index = 0;
        datas.forEach(d => {
          let downloadUrl = getWorkflowPdf(d.ApplicantUserID, d.ProcessGroupName, d.ProcessID, d.BizFormID);

          httpConfigs.push({
            method: method.value,
            url: downloadUrl,
            headers: {},
            key: index,
            begin: new Date().getTime(),
            responseType: fileMode.value ? 'blob' : 'json',
            fileName: `${d.ProcessGroupName}-${d.SerialNumber}.pdf`
          });

          results[index] = {
            at: getDateTime(),
            content: `下载文件：${d.ProcessGroupName}-${d.SerialNumber}.pdf`,
            messages: [`[${getDateTime()}] 开始下载文件...`]
          };

          index++;
        });
      }

      if (meta.type == 'single') {
        let d = res.data;
        let downloadUrl = getWorkflowPdf(d.ApplicantUserID, d.ProcessGroupName, d.ProcessID, d.ID);

        for (let index = 0; index < times.value; index++) {
          httpConfigs.push({
            method: method.value,
            url: downloadUrl,
            headers: {},
            key: index,
            begin: new Date().getTime(),
            responseType: fileMode.value ? 'blob' : 'json',
            fileName: `${d.ProcessGroupName}-${d.SerialNumber}-${index + 1}.pdf`
          });

          results[index] = {
            at: getDateTime(),
            content: `下载文件：${d.ProcessGroupName}-${d.SerialNumber}-${index + 1}.pdf`,
            messages: [`[${getDateTime()}] 开始下载文件...`]
          };
        }
      }

      invokeHttpRequest(httpConfigs);
    });
  }

  function invokeHttpRequest(httpConfigs) {
    let tasks = [];
    let begin = new Date().getTime();

    (httpConfigs || []).forEach(config => {
      let task = $http(config)
        .then(rs => {
          let end = new Date().getTime();

          if (fileMode.value) {
            let url = window.URL.createObjectURL(new Blob([rs]));

            let link = document.createElement('a');
            let fileName = config.fileName || `datas.${method.value == 'iworkflow_pdf' ? 'pdf' : 'json'}`;

            link.href = url;
            link.setAttribute('style', 'display:none;');
            link.setAttribute('download', fileName);

            document.body.appendChild(link);
            link.click();

            document.body.removeChild(link);

            (results[config.key] || { messages: [] }).messages.push(`${getDateTime()} 下载完成`);
          } else {
            (results[config.key] || { messages: [] }).messages.push(`${getDateTime()} 接口执行完成`);

            if (times.value == 1) {
              $copyText(JSON.stringify(rs, null, 2)).then(() => {
                $message.success('结果已复制！');
              });
            }
          }

          (results[config.key] || {}).status = 'success';
          (results[config.key] || {}).elapsedTicks = (end - (config.begin || 0)) / 1000;
        })
        .catch(err => {
          let end = new Date().getTime();

          (results[config.key] || {}).status = 'error';
          (results[config.key] || {}).elapsedTicks = (end - (config.begin || 0)) / 1000;
          (results[config.key] || { messages: [] }).messages.push(`${getDateTime()} 出现错误 - ${JSON.stringify(err)}`);
        });

      tasks.push(task);
    });

    Promise.all(tasks)
      .then(rs => {
        let end = new Date().getTime();

        callStatistics.value = (end - begin) / 1000;

        $message.success('执行成功！');
      })
      .catch(err => {
        let end = new Date().getTime();

        callStatistics.value = (end - begin) / 1000;

        $message.success('出现错误，' + err);
      });
  }

  function addTestHistory() {
    $http.post('/api/system/params/create', { category: 'PressureTest', value: api.value }).then(res => {});
  }

  function getHttpUrl(uri) {
    let url = uri || api.value;

    if (url.indexOf('?') == -1) {
      url += '?';
    } else {
      url += '&';
    }

    let keyValues = paramsRef.value.build();
    let props = Object.getOwnPropertyNames(keyValues);

    for (let item of props) {
      url += `${item}=${keyValues[item]}&`;
    }

    url = url.substring(0, url.length - 1);

    return url;
  }

  function resolveRequest(items, obj) {
    let props = Object.getOwnPropertyNames(obj);

    props.forEach(p => {
      let item = { key: p };

      if (typeof obj[p] != 'object') {
        item.value = obj[p];
      } else {
        item.value = obj[p].value || '';
      }

      if (obj[p].ig) {
        item.ig = obj[p].ig;
      }

      if (obj[p].items) {
        item.items = obj[p].items;
        item.value = item.items[0].value;
      }

      items.push(item);
    });
  }

  function getWorkflowPdf(userId, groupName, processId, bizFormId) {
    let host = api.value;
    let keyValues = bodysRef.value.getFullValues();

    let webHost = keyValues.portal;
    let processGroupName = (groupName || '').toLowerCase();

    if (processGroupName.indexOf('leo') != -1) {
      webHost += `/Process/MyWorkFlow/AopForm`;
    } else if (processGroupName.indexOf('clsa3.0') != -1) {
      webHost += `/Seal/Application/FormDetail`;
    } else if (processGroupName.indexOf('clsa') != -1) {
      webHost += `/Process/UseBySeal/SealByBorrowDetail`;
    } else {
      webHost += `/Process/MyWorkFlow/AopFormCommon`;
    }

    webHost += `?ProcessID=${processId}|BizFormId=${bizFormId}|UserId=${userId}${keyValues.pdfMode || ''}`;

    let pdfUrl = `${host}/api/Pdf/GenerateForUrl?fileName=表单_${bizFormId}&url=${webHost}`;

    return getHttpUrl(pdfUrl);
  }

  function loadHistories() {
    $http.get(`/api/system/params/PressureTest`).then(res => {
      histories.value = res.datas.map(d => {
        return { value: d.Value };
      });
    });
  }

  onMounted(() => {
    (configs || []).forEach(c => {
      templates.value.push({ text: c.name, value: c.name });
    });

    ResizeChartPanel($store.state.area.content);

    $eventBus.on('windowResizeChanged', client => ResizeChartPanel(client));

    let tabIndex = 0;
    let tabs = ['authorization', 'headers', 'body', 'params'];

    let handler = setInterval(() => {
      if (tabIndex >= tabs.length) {
        clearInterval(handler);
      }

      activeKey.value = tabs[tabIndex++];
    }, 5);

    loadHistories();
  });
</script>

<style lang="less" scoped>
  div.shell {
    font-size: 14px;
    user-select: none;
    font-family: 'Courier New', Courier, monospace;

    > div {
      margin: 0;
      padding: 5px 5px;
      line-height: 22px;
      width: 100%;
      display: inline-block;
      background: #fff;
      display: inline-block;
      margin-bottom: 5px;
      border-radius: 5px;
    }

    &.headers {
      width: 100%;
      display: inline-block;

      > p {
        margin: 0;
        &:last-of-type {
          margin-top: 0;
          color: #333;
          padding: 5px 15px;
          border-radius: 5px 5px 0 0;
          background-color: #fff;
        }
      }
    }

    &.results {
      height: 100%;
      overflow: auto;
      max-height: 388px;
      user-select: none;

      > :deep(div) {
        position: relative;

        &:first-of-type {
          border-radius: 0 0 5px 5px;
        }

        > b {
          width: 30px;
          height: 30px;
          line-height: 30px;
          vertical-align: middle;
          display: inline-block;
          text-align: center;
          position: absolute;
          border-radius: 50%;
          font-size: 12px;
          color: #fff;
          background: #333;
          left: 2px;
          top: 2px;
        }

        > h3 {
          padding-left: 35px;
          word-break: break-all;
          text-overflow: ellipsis;
          font-size: 14px;
          line-height: 12px;
          font-weight: bolder;

          > b {
            margin-right: 10px;
          }
        }

        > ul {
          margin: 0;
          margin-left: 20px;
          list-style-type: decimal;
        }

        &.success {
          color: #fff;
          background-color: #198754;
          border-color: #198754;

          > b {
            color: #000;
            background-color: #0dcaf0;
          }

          > h3 {
            color: #fff;
          }
        }

        &.error {
          color: #fff;
          background-color: #ff4d4f;
          border-color: #ff4d4f;

          > b {
            color: #000;
            background-color: #0dcaf0;
          }

          > h3 {
            color: #fff;
          }
        }
      }
    }
  }
</style>
